#include <Client2.h>
#include <cstring>
#include <iostream>
#include <openssl\rc4.h>
using std::cout;
using std::cerr;
using std::endl;
using std::fill;

Client::Client(int pType, int pPort): Type(pType), client_port(pPort)
{
	#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
	{
		cerr << "WSAStartup failed." << endl;
		exit(1);
	}
	#endif
}

// Socket function wrappers.
int Client::CreateSocket(int pType)			// 0 = TCP, 1 = UDP; default is to create TCP socket.
{
	if (pType == TCPSOCKET)
	{
		Type = TCPSOCKET;
		error_check = client_socket_FD = (int)socket(AF_INET, SOCK_STREAM, 0);
	}
	else
	{
		Type = UDPSOCKET;
		error_check = client_socket_FD = (int)socket(AF_INET, SOCK_DGRAM, 0);
	}
	if (error_check == -1)
	{
		cerr << "ERROR001: Creating socket of type " << pType << endl;
	}
	return error_check;
}

int Client::InitialiseAddress(int pPort)	// Default Client port is 6001.
{
	client_port = pPort;
	// Client address initialisation for binding.
	client_address.sin_family = AF_INET;				// Socekt family.
	client_address.sin_addr.s_addr = INADDR_ANY;		// Setting client IP. INADDR_ANY is blank IP.
	client_address.sin_port = htons(client_port);	// Setting client port.
	fill((char*)&(client_address.sin_zero), (char*)&(client_address.sin_zero)+8, '\0');
	return 0;
}

int Client::SetSocketOptions()					// Set socket options to reuse address.
{
	int Yes = 1;
	error_check = setsockopt(client_socket_FD, SOL_SOCKET, SO_REUSEADDR, (char*)&Yes, sizeof (int));
	if (error_check == -1)
	{
		cerr << "ERROR002: Setting socket options. " << endl;
	}
	return error_check;
}

int Client::Bind()								// Bind Client socket with address.
{
	error_check = bind(client_socket_FD, (sockaddr*)&client_address, sizeof(client_address));
	if (error_check == -1)
	{
		cerr << "ERROR002: Binding." << endl;
	}
	return error_check;
}

int Client::Connect(char* pServername, int pServerPort)
{
	struct hostent* Servername;		// Server name/IP.
	if ((Servername = gethostbyname(pServername)) == NULL)
	{
		cerr << "ERROR003: Getting server name/IP" << endl;
		return -1;
	}

	// Initializing Server address to connect to.
	server_address.sin_family = AF_INET;							// Socket family.
	server_address.sin_addr = *((in_addr*)(*Servername).h_addr);	// Server name/IP.
	server_address.sin_port = htons(pServerPort);			// Server port provided as argument.
	fill((char*)&(server_address.sin_zero), (char*)&(server_address.sin_zero)+8, '\0');

	// Connecting to the server.
	error_check = connect(client_socket_FD, (sockaddr*)&server_address, sizeof(server_address));
	if (error_check == -1)
	{
		cerr << "ERROR004: Connecting " << endl;
	}
	return error_check;
}

int Client::Receive()
{
	error_check = num_of_bytes_received = recv(client_socket_FD, buffer, MAXBUFFERSIZE-1, 0);
	if (error_check == -1)
	{
		cerr << "ERROR005 Receiveing" << endl;
	}
	buffer[num_of_bytes_received] = '\0';
	return error_check;
}

int Client::Receive(void* Data, int DataSize)
{
	error_check = num_of_bytes_received = recv(client_socket_FD, (char*)Data, DataSize, 0);
	if (error_check == -1)
	{
		cerr << "ERROR005 Receiveing" << endl;
	}
	return error_check;
}

int Client::Send(void* Data, int DataSize)
{
	error_check = num_of_bytes_sent = send(client_socket_FD, (char*)Data, DataSize, 0);
	if (error_check == -1)
	{
		cerr << "ERROR006: Client Sending. " << endl;
	}
	return error_check;
}

// UDP, sendto (data, datasize, IP/name, port);
int Client::SendTo (void* Data, int DataSize)
{
	error_check = num_of_bytes_sent = sendto(client_socket_FD, (char*)Data, DataSize, 0, (sockaddr*)&their_address, sizeof(their_address));
	if (error_check == -1)
	{
		cerr << "ERROR008: Client Sending to. " << endl;
	}
	return error_check;
}
int Client::SendTo(void* Data, int DataSize, char* pTheirIP, int pTheirPort)
{
	struct hostent* TheirIP;		// Client name/IP.
	if ((TheirIP = gethostbyname(pTheirIP)) == NULL)
	{
		cerr << "ERROR009: Getting Their name/IP" << endl;
		return -1;
	}
	// Initializing Their address to send to.
	their_address.sin_family = AF_INET;							// Socket family.
	their_address.sin_addr = *((in_addr*)(*TheirIP).h_addr);		// Their name/IP.
	their_address.sin_port = htons(pTheirPort);			// Their port provided as argument.
	fill((char*)&(their_address.sin_zero), (char*)&(their_address.sin_zero)+8, '\0');

	error_check = num_of_bytes_sent = sendto(client_socket_FD, (char *)Data, DataSize, 0, (sockaddr*)&their_address, sizeof(their_address));
	if (error_check == -1)
	{
		cerr << "ERROR010: Client Sending to. " << endl;
	}
	return error_check;
}
// recvfrom ();
int Client::RecvFrom()
{
	socklen_t their_addressSize = sizeof(their_address);
	error_check = num_of_bytes_received = recvfrom (client_socket_FD, buffer, MAXBUFFERSIZE-1, 0, (sockaddr*)&their_address, &their_addressSize);
	if (error_check == -1)
	{
		cerr << "ERROR011 Receiveing" << endl;
		return error_check;
	}
	buffer[num_of_bytes_received] = '\0';
	cout << "Client got packet from " << inet_ntoa(their_address.sin_addr) << " on socket " << client_socket_FD << endl;
	return error_check;
}
int Client::RecvFrom(void* Data, int DataSize)
{
	socklen_t their_addressSize = sizeof(their_address);
	error_check = num_of_bytes_received = recvfrom (client_socket_FD, (char*)Data, DataSize, 0, (sockaddr*)&their_address, &their_addressSize);
	if (error_check == -1)
	{
		cerr << "ERROR011 Receiveing" << endl;
		return error_check;
	}
	cout << "Client got packet from " << inet_ntoa(their_address.sin_addr) << " on socket " << client_socket_FD << endl;
	return error_check;
}
int Client::CloseClientSocket()
{
	error_check = close(client_socket_FD);
	return error_check;
}

// Additional Functions.
int Client::DisplayServerInfo()
{
	cout << "Server Address: " << inet_ntoa(server_address.sin_addr) << endl;
	cout << "Server Port   : " << ntohs(server_address.sin_port) << endl;
	return 0;
}

int Client::DisplayClientInfo()
{
	cout << "Client Address: " << inet_ntoa(client_address.sin_addr) << endl;
	cout << "Client Port   : " << ntohs(client_address.sin_port) << endl;
	cout << "Client Socket : " << client_socket_FD << endl;
	return 0;
}

int Client::DisplayTheirInfo()
{
	cout << "Their Address: " << inet_ntoa(their_address.sin_addr) << endl;
	cout << "Their Port   : " << ntohs(their_address.sin_port) << endl;
	return 0;
}

int Client::GetNumOfBytesSent()
{
	return num_of_bytes_sent;
}

int Client::GetNumOfBytesReceived()
{
	return num_of_bytes_received;
}

char* Client::GetBuffer()
{
	return buffer;
}



/***Encrypt Function***/
char* Encrypt(unsigned char *keydata, char *plaintext, int Length)
{
	char *encrypted = new char[Length+1];
	RC4_KEY key;
	RC4_set_key(&key, strlen((const char*)keydata), keydata);

	// Encryption
	RC4(&key, Length, (const unsigned char*)plaintext, (unsigned char*)encrypted);
	encrypted[Length] = '\0';

	return encrypted;
}
